import uuid,base64,json
from datetime import datetime


from flask import request
from flask_jwt_extended import \
    get_jwt_identity,\
    jwt_required,\
    create_access_token,\
    create_refresh_token

from .marshmallow import MobileSchema,ValidationError,UserSchema
from .models import User,db
from application import message,status
from application.utils.captcha import \
    check_captcha,\
    CaptchaError,\
    CaptchaParamsError,\
    CaptchaNetWorkError,\
    CaptchaFailError
from application import oss,redis
from application.services import users_services

def check_mobile(mobile):

    """验证手机号码是否已经注册"""
    ms = MobileSchema()
    try:
        ms.load({"mobile": mobile})
        ret = {"errno": status.CODE_OK, "errmsg": message.ok}
    except ValidationError as e:
        ret = {"errno": status.CODE_VALIDATE_ERROR, "errmsg": e.messages["mobile"][0]}

    return ret

def register(mobile,password,password2, sms_code):
    """用户基本信息注册"""
    # 1. 验证手机是否已经注册
    ret = check_mobile(mobile)
    if ret["errno"] != 1000:
        return ret

    # 2. 验证并保存用户信息
    try:
        us = UserSchema()
        instance = us.load({
                "mobile":mobile,
                "password":password,
                "password2":password2,
                "sms_code": sms_code
            })
        ret = {"errno": status.CODE_OK, "errmsg":message.ok,"data": us.dump(instance)}

    except ValidationError as e:
        if e.messages.get("sms_code"):
            errmsg = e.messages["sms_code"][0]
        elif e.messages.get("password"):
            errmsg = e.messages["password"][0]
        else:
            errmsg = "验证失败！"

        ret = {"errno": status.CODE_VALIDATE_ERROR, "errmsg": errmsg}

    # 4. 返回结果
    return ret

def login(account, password, ticket,randstr):
    """
    jwt登录
    :param account: 登录账户[手机号、邮箱、用户名]
    :param password: 登录密码
    :param ticket: 验证码验证成功以后回调的凭据
    :param randstr: 验证码验证成功以后回调的随机数
    :return: access_token登录令牌refresh_token登录凭据
    """
    # 0. 验证防水墙回调的凭据和随机数
    try:
        check_captcha(ticket,randstr,request.remote_addr)
    except CaptchaError:
        return {"errno": status.CODE_VALIDATE_ERROR, "errmsg": message.validate_fail}

    # 1. 通过数据库进行账号和密码的匹配
    if len(account)<1 or len(password) < 1:
        return {
            "errno":status.CODE_MISSING_PARAMS,
            "errmsg": message.no_account_or_password,
        }

    user = users_services.check_user_by_account(account)
    if user is None:
        return {
            "error": status.CODE_NO_SUCH_USER,
            "errmsg": message.no_user
        }

    if not user.check_password(password):
        return {
            "error": status.CODE_VALIDATE_ERROR,
            "errmsg": message.password_is_error
        }

    # 数据库中用户信息更新
    user.update_login_log()

    # 模型转字典
    user_data = user.__todict__()
    # 缓存登录信息
    try:
        users_services.save_user_data_to_cache(user_data)
    except users_services.DataSaveError:
        return {
            "error": status.CODE_CACHE_ERROR,
            "errmsg": message.cache_data_error
        }
    # 2. 生成jwt assess token 和 refresh token
    access_token = create_access_token(identity=user_data)    # identity 就是载荷
    refresh_token = create_refresh_token(identity=user_data)

    # 3. 返回2个token给客户端
    return {
        "errno":status.CODE_OK,
        "errmsg": message.ok,
        "access_token": access_token,
        "refresh_token": refresh_token
    }

@jwt_required() # 验证 jwt的 assess token
def info():
    """获取当前用户详细信息"""
    user_data = get_jwt_identity()
    return {
        "errno": status.CODE_OK,
        "errmsg": message.ok,
        "data": {
            **user_data
        }
    }

@jwt_required(refresh=True) # 验证 jwt的 refresh token
def refresh():
    """依靠refresh token 换取 assess token"""
    user_data = get_jwt_identity() # 获取refresh token中的载荷
    try:
        # 从缓存中获取用户信息
        user_data = users_services.get_data_by_cache(f"user_{user_data['id']}")
        user_data = json.loads(user_data)
    except users_services.DataNotExists:
        # 如果缓存中没有用户信息,则到数据中提取最新的
        user = users_services.check_user_by_account(user_data["name"])
        if user is None:
            return {
                "errno": status.CODE_NO_SUCH_USER,
                "errmsg": message.no_user,
            }

        # 再次创建缓存
        user_data = user.__todict__()

    try:
        users_services.save_user_data_to_cache(user_data)
    except users_services.DataSaveError:
        return {
            "error": status.CODE_CACHE_ERROR,
            "errmsg": message.cache_data_error
        }

    access_token = create_access_token(identity=user_data)
    return {
        "errno": status.CODE_OK,
        "errmsg": message.ok,
        "access_token":access_token
    }

@jwt_required()
def avatar(image):
    """头像更新"""
    user_data = get_jwt_identity()
    user = users_services.check_user_by_account(user_data["name"])
    if user is None:
        return {
            "errno": status.CODE_NO_SUCH_USER,
            "errmsg": message.no_user,
        }

    try:
        data_list = image.split(";base64,")
        data_type = data_list[0].split("/")[-1] # 文件后缀
        data_b64 = data_list[-1] # base64编码后的文件内容
        # 生成一个文件名
        filename = uuid.uuid4()
        # 拼接成完整名称
        file = "%s.%s" % (filename,data_type)
        b64_image = base64.b64decode(data_b64)
        # 上传文件到阿里云对象存储中
        ret = oss.upload(file,b64_image)
        if ret.status != 200:
            raise Exception

        avatar_url = "%s/%s" % (oss.bucket_url,file)

        # 更新数据
        user.avatar = avatar_url
        db.session.commit()

        # 同时还有同步客户端的信息
        user_data = user.__todict__()
        access_token = create_access_token(user_data)
        refresh_token = create_refresh_token(user_data)

        try:
            users_services.save_user_data_to_cache(user_data)
        except users_services.DataSaveError:
            return {
                "error": status.CODE_CACHE_ERROR,
                "errmsg": message.cache_data_error
            }

        return {
            "errno": status.CODE_OK,
            "errmsg": message.ok,
            "access_token": access_token,
            "refresh_token": refresh_token
        }

    except Exception:
        return {
            "errno": status.CODE_FILE_ERROR,
            "errmsg": message.upload_file_error,
        }

@jwt_required()
def password(old_password,new_password,re_password):
    """
    修改密码
    :param old_password: 原密码
    :param new_password: 新密码
    :param re_password:  确认新密码
    :return:
    """
    if new_password != re_password:
        return {
            "errno": status.CODE_VALIDATE_ERROR,
            "errmsg": message.password_not_match
        }

    user_data = get_jwt_identity()
    user = users_services.check_user_by_account(user_data["name"])
    if user is None:
        return {
            "errno": status.CODE_NO_SUCH_USER,
            "errmsg": message.no_user,
        }

    ret = user.check_password(old_password)
    if not ret:
        return {
            "errno": status.CODE_PASSWORD_ERROR,
            "errmsg": message.password_is_error
        }

    user.password = new_password
    db.session.commit()

    return {
        "errno": status.CODE_OK,
        "errmsg": message.ok
    }

@jwt_required()
def transation_password(old_password,new_password,re_password):
    """
    修改交易密码
    :param old_password: 原交易密码
    :param new_password: 新交易密码
    :param re_password:  确认新交易密码
    :return:
    """
    if new_password != re_password:
        return {
            "errno": status.CODE_VALIDATE_ERROR,
            "errmsg": message.password_not_match
        }

    user_data = get_jwt_identity()
    user = users_services.check_user_by_account(user_data["name"])
    if user is None:
        return {
            "errno": status.CODE_NO_SUCH_USER,
            "errmsg": message.no_user
        }

    ret = user.check_transation_password(old_password)
    if not ret:
        return {
            "errno": status.CODE_PASSWORD_ERROR,
            "errmsg": message.password_is_error
        }

    user.transation_password = new_password
    db.session.commit()

    return {
        "errno": status.CODE_OK,
        "errmsg": message.ok
    }

@jwt_required()
def nickname(nick_name):
    """
    修改昵称
    :param nick_name: 昵称
    :return:
    """
    user_data = get_jwt_identity()
    user = users_services.check_user_by_account(user_data["name"])
    if user is None:
        return {
            "errno": status.CODE_NO_SUCH_USER,
            "errmsg": message.no_user
        }

    user.nickname = nick_name
    db.session.commit()

    user_data = user.__todict__()
    access_token = create_access_token(user_data)
    refresh_token = create_refresh_token(user_data)

    try:
        users_services.save_user_data_to_cache(user_data)
    except users_services.DataSaveError:
        return {
            "error": status.CODE_CACHE_ERROR,
            "errmsg": message.cache_data_error
        }

    return {
        "errno": status.CODE_OK,
        "errmsg": message.ok,
        "access_token": access_token,
        "refresh_token": refresh_token
    }


